/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pfc.generacion;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ddlutils.model.Column;
import org.apache.ddlutils.model.Database;
import org.apache.ddlutils.model.Table;
import pfc.comun.IDEpfc;
import pfc.comun.Proyecto;
import pfc.modelocg.CGModel;
import pfc.modeloer.EREntity;
import pfc.modeloer.ERModel;

/**
 *
 * @author xandru
 */
public class GeneradorDAO {

    Proyecto _proyecto;
    Database _dbmodel;
    ERModel _erModel;
    CGModel _cgModel;

    public GeneradorDAO(Proyecto pro, Database db, ERModel er, CGModel cg) {
        this._proyecto = pro;
        this._dbmodel = db;
        this._erModel = er;
        this._cgModel = cg;
    }

    public void generarDAOs(File srcPath) {
        IDEpfc.log("\n================= Generando clases DAO ========================");
        File f = new File(srcPath.getAbsolutePath() + File.separator + "dao");

        try {
            FileUtils.forceMkdir(f);
        } catch (IOException ex) {
            Logger.getLogger(Application.class.getName()).log(Level.SEVERE, null, ex);
        }

        generarDAOExceptions(f);

        generarBaseDAO(f);
        generarDAOFactory(f);


        for (Table t : _dbmodel.getTables()) {
            if (t.getName().startsWith("_"))
                continue;
            generarDAOFile(f, t);
        }
    }

    private void generarDAOFile(File f, Table t) {


        String clase = StringUtils.capitalize(t.getName() + "DAO");
        IDEpfc.logTime("Generando clase " + clase);
        EREntity ent = this._erModel.findEntity(t.getName());

        ArrayList<String> imports = new ArrayList<String>();
        ArrayList<String> fields = new ArrayList<String>();
        ArrayList<String> cl = new ArrayList<String>();
        ArrayList<String> methods = new ArrayList<String>();

        imports.add("package " + _proyecto.nombre + ".dao;\n");

        addImports(imports);
        addFields(fields);
        addConstructor(methods, clase);
        addCreateEntityMethod(methods, t);
        addRemoveEntityMethod(methods, t);
        addUpdateEntityMethod(methods, t);
        addFinderMethods(methods, t);
        addFindListMethod(methods, t);

        if (ent.getErElementType() == EREntity.EntityType.Master) {
            addGetAllMethod(methods, t);
        }

        try {
            PrintWriter dto = new PrintWriter(new FileOutputStream(f.getAbsolutePath() + File.separator + clase + ".java"));

            for (String line : imports) {
                dto.println(line);
            }
            dto.println("public class " + clase + " extends BaseDAO implements Cloneable {");
            for (String line : fields) {
                dto.println(line);
            }
            for (String line : methods) {
                dto.println(line);
            }
            dto.println("}");

            dto.flush();
            dto.close();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Application.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void addConstructor(ArrayList<String> metodos, String clase) {
        metodos.add("public " + clase + "(DataSource ds) {");
        metodos.add("this.dataSource = ds;");
        metodos.add("}");
        metodos.add("\n");
    }

    private void addImports(ArrayList<String> imports) {
        String[] aImportar = {_proyecto.nombre + ".dto.*",
            "org.apache.commons.dbutils.QueryRunner",
            "org.apache.commons.dbutils.ResultSetHandler",
            "org.apache.commons.dbutils.handlers.BeanHandler",
            "org.apache.commons.dbutils.handlers.BeanListHandler",
            "javax.sql.DataSource",
            "java.sql.SQLException",
            "java.util.ArrayList",
            "java.util.List",
            "pfc.core.db.*"};

        for (String p : aImportar) {
            imports.add("import " + p + ";");
        }
        imports.add("\n");

    }

    private void generarDAOExceptions(File f) {

        String[] exceptions = {"CreateException", "DuplicateKeyException", "FinderException", "ObjectNotFoundException", "NoSuchEntityException"};
        try {
            for (String ex : exceptions) {
                IDEpfc.logTime("Generando clase " + ex);
                PrintWriter exf = new PrintWriter(new FileOutputStream(f.getAbsolutePath() + File.separator + ex + ".java"));
                exf.println("package " + _proyecto.nombre + ".dao;");
                exf.println();
                exf.println("public class " + ex + " extends Exception {\n");
                exf.println("public " + ex + "(String message) {\n");
                exf.println("super(message);");
                exf.println("}");
                exf.println("}\n");
                exf.flush();
                exf.close();
            }
        } catch (FileNotFoundException ex) {
            Logger.getLogger(GeneradorDAO.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void addCreateEntityMethod(ArrayList<String> methods, Table t) {
        methods.add("public void create(" + StringUtils.capitalize(t.getName()) + " obj) throws CreateException {");

        String sqlcampos = "";
        String sqlvalues = "";
        String metodosObj = "";

        int i = 0;
        for (Column c : t.getColumns()) {
            i++;
            if (c.isPrimaryKey() && (c.isAutoIncrement())) {
                continue;
            }

            sqlcampos += c.getName();
            sqlvalues += "?";
            metodosObj += "obj.get" + StringUtils.capitalize(c.getName()) + "()";

            if (i != t.getColumnCount()) // Ultima columna
            {
                sqlcampos += ",";
                sqlvalues += ",";
                metodosObj += ",";
            }
        }

        String sql = "insert into " + t.getName() + "(" + sqlcampos + ") values (" + sqlvalues + ")";

        methods.add("QueryRunner run = new QueryRunner(dataSource);");
        methods.add("try {");
        methods.add("int inserts = run.update(\"" + sql + "\"," + metodosObj + ");");
        methods.add("} catch (SQLException sqle) {");
        methods.add("System.out.println(sqle.getMessage());");
        methods.add("}");

        methods.add("}");
    }

    private void addFields(ArrayList<String> fields) {
        fields.add("DataSource dataSource;");
    }

    private void addRemoveEntityMethod(ArrayList<String> methods, Table t) {
        Column[] pKeys = t.getPrimaryKeyColumns();
        String sqlStr = "DELETE FROM " + t.getName() + " WHERE ";
        String sqlParams = "";
        methods.add("public void remove(PrimaryKey pk)");
        methods.add("{");

        if (pKeys.length == 1) {
            sqlStr += pKeys[0].getName() + "=?";
            if (pKeys[0].isOfNumericType()) {
                sqlParams = "pk.getIntegerValue()";
            } else {
                sqlParams = "pk.getStringValue()";
            }
        }

        if (sqlParams.equals("")) {
            methods.add("}");
            methods.add("\n");
            return;
        }

        methods.add("QueryRunner run = new QueryRunner(dataSource);");
        methods.add("try {");
        methods.add("int delete = run.update(\"" + sqlStr + "\"," + sqlParams + ");");
        methods.add("} catch (SQLException sqle) {");
        methods.add("System.out.println(sqle.getMessage());");
        methods.add("}");
        methods.add("}");
        methods.add("\n");

    }

    private void addUpdateEntityMethod(ArrayList<String> methods, Table t) {

        String sqlStr = "UPDATE " + t.getName() + " SET ";
        String sqlParams = "";
        String strWhere = "";

        int i = 0;
        for (Column c : t.getColumns()) {
            i++;
            if (c.isPrimaryKey()) {
                continue;
            }

            sqlStr += c.getName() + "=?";
            sqlParams += ",obj.get" + StringUtils.capitalize(c.getName()) + "()";

            if (i != t.getColumnCount()) // Ultima columna
            {
                sqlStr += ",";
                //sqlParams += ",";
            }
        }



        methods.add("public void update(PrimaryKey pk," + StringUtils.capitalize(t.getName()) + " obj)");
        methods.add("{");

        Column[] pKeys = t.getPrimaryKeyColumns();
        i = 0;
        for (Column key : pKeys) {
            i++;
            if (i == 1) {
                strWhere += " WHERE " + key.getName() + "=?";
            } else {
                strWhere += " and " + key.getName() + "=?";
            }

            if (key.isOfNumericType()) {
                sqlParams += ",pk.getIntegerValue()";
            } else {
                sqlParams += ",pk.getStringValue()";
            }

        }

        sqlStr += strWhere;
        methods.add("QueryRunner run = new QueryRunner(dataSource);");
        methods.add("try {");
        methods.add("int update = run.update(\"" + sqlStr + "\"" + sqlParams + ");");
        methods.add("} catch (SQLException sqle) {");
        methods.add("System.out.println(sqle.getMessage());");
        methods.add("}");


        methods.add("}");
        methods.add("\n");
    }

    private void addFinderMethods(ArrayList<String> methods, Table t) {
        String sqlStr = "SELECT * from " + t.getName() + " WHERE 1=1";
        String sqlParams = "";
        methods.add("public " + StringUtils.capitalize(t.getName()) + " findByPrimaryKey (PrimaryKey pk)");
        methods.add("{");


        Column[] pKeys = t.getPrimaryKeyColumns();
        int i = 0;
        for (Column key : pKeys) {
            i++;
            sqlStr += " and " + key.getName() + "=?";
            if (key.isOfNumericType()) {
                sqlParams += ",pk.getIntegerValue()";
            } else {
                sqlParams += ",pk.getStringValue()";
            }

            /*
            if (i!=pKeys.length)
            {
            sqlParams += ",";
            }
             */
        }
        methods.add("QueryRunner run = new QueryRunner(dataSource);");
        methods.add("try {");
        methods.add("ResultSetHandler<" + StringUtils.capitalize(t.getName()) + "> h = new BeanHandler<" + StringUtils.capitalize(t.getName()) + ">(" + StringUtils.capitalize(t.getName()) + ".class);");
        methods.add(StringUtils.capitalize(t.getName()) + " obj = run.query(\"" + sqlStr + "\",h" + sqlParams + ");");
        methods.add("return obj;");
        methods.add("} catch (SQLException sqle) {");
        methods.add("System.out.println(sqle.getMessage());");
        methods.add("return null;");
        methods.add("}");

        methods.add("}");
        methods.add("\n");
    }

    private void addFindListMethod(ArrayList<String> methods, Table t) {
        ArrayList<Object> sqlParams = new ArrayList<Object>();
        String beanName = StringUtils.capitalize(t.getName());
        methods.add("public List<" + beanName + "> find" + beanName + "List (" + beanName + " obj)");
        methods.add("{");
        methods.add("String sql=\"select *  from " + t.getName() + " where 1=1\";");
        methods.add("ArrayList<Object> params = new ArrayList<Object>();");

        int i = 0;
        for (Column key : t.getColumns()) {
            String fieldCap = StringUtils.capitalize(key.getName());
            methods.add("if (obj.get" + fieldCap + "()!=null) {");
            methods.add("sql += \" and " + key.getName() + " = ?\";");
            methods.add("params.add(obj.get" + fieldCap + "());");
            methods.add("}");
        }

        methods.add("QueryRunner run = new QueryRunner(dataSource);");
        methods.add("try {");
        methods.add("ResultSetHandler<List<" + beanName + ">> h = new BeanListHandler<" + beanName + ">(" + beanName + ".class);");
        methods.add("List<" + beanName + "> lista = run.query(sql,h,params.toArray());");
        methods.add("return lista;");
        methods.add("} catch (SQLException sqle) {");
        methods.add("System.out.println(sqle.getMessage());");
        methods.add("return null;");
        methods.add("}");

        methods.add("}");
        methods.add("\n");
    }

    private void generarDAOFactory(File f) {
        String clase = "DAOFactory";

        IDEpfc.logTime("Generando " + clase);

        try {
            PrintWriter dao = new PrintWriter(new FileOutputStream(f.getAbsolutePath() + File.separator + clase + ".java"));
            dao.println("package " + _proyecto.nombre + ".dao;\n");

            dao.println("import javax.sql.DataSource;");
 //           dao.println("import pfc.core.db.BaseDAO;");

            dao.println("public class " + clase + " {");
            dao.println("public static BaseDAO newDAO(String clase,DataSource ds){");
            dao.println("System.out.println(clase);");
            for (Table t : this._dbmodel.getTables()) {
                if (t.getName().startsWith("_"))
                    continue;
                String daoClass = StringUtils.capitalize(t.getName()) + "DAO";
                dao.println("if (clase.equals(\"" + daoClass + "\")) return new " + daoClass + "(ds);");
            }
            dao.println("return null;");
            dao.println("}");

            dao.println("}");
            dao.flush();
            dao.close();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private void addGetAllMethod(ArrayList<String> methods, Table t) {
        String beanName = StringUtils.capitalize(t.getName());
        methods.add("public List<" + beanName + "> getAll()");
        methods.add("{");
        methods.add("String sql=\"select *  from " + t.getName() + "\";");

        methods.add("QueryRunner run = new QueryRunner(dataSource);");
        methods.add("try {");
        methods.add("ResultSetHandler<List<" + beanName + ">> h = new BeanListHandler<" + beanName + ">(" + beanName + ".class);");
        methods.add("List<" + beanName + "> lista = run.query(sql,h);");
        methods.add("return lista;");
        methods.add("} catch (SQLException sqle) {");
        methods.add("System.out.println(sqle.getMessage());");
        methods.add("return null;");
        methods.add("}");

        methods.add("}");
        methods.add("\n");
    }

    private void generarBaseDAO(File f) {
        String clase = "BaseDAO";


        try {
            PrintWriter dao = new PrintWriter(new FileOutputStream(f.getAbsolutePath() + File.separator + clase + ".java"));
            dao.println("package " + _proyecto.nombre + ".dao;\n");
            dao.println("import " + _proyecto.nombre + ".dto.BaseDTO;\n");
            dao.println("import pfc.core.db.*;");
            dao.println("public abstract class BaseDAO {");
            dao.println("abstract public BaseDTO findByPrimaryKey (PrimaryKey pk);");

            dao.println("}");
            dao.flush();
            dao.close();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
